﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Common.Extend
{
    public static class ExDataTable
    {
        /// <summary>
        /// 将DataTable数据转成List对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static List<T> ExToList<T>(this DataTable dt) where T : new()
        {
            if (dt == null)
            {
                throw new ArgumentNullException("Table is null.");
            }
            try
            {
                return DataTableToList<T>.Trans(dt);
            }
            catch
            {
                List<T> list = new List<T>();
                Type type = typeof(T);
                PropertyInfo[] pros = typeof(T).GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance);
                foreach (System.Data.DataRow row in dt.Rows)
                {
                    T m = Activator.CreateInstance<T>();
                    foreach (PropertyInfo pro in pros)
                    {
                        if (dt.Columns.Contains(pro.Name))
                        {
                            object colValue = row[pro.Name];
                            if (colValue != null && !(colValue is DBNull))
                            {
                                pro.SetValue(m, colValue, null);
                            }
                        }
                    }
                    list.Add(m);
                }
                return list;
            }
        }
        /// <summary>
        /// 将DataTable转成数组
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static ArrayList ExToArrayList(this DataTable dt)
        {
            ArrayList list = new ArrayList();
            foreach (System.Data.DataRow dr in dt.Rows)
            {
                Dictionary<string, object> drow = new Dictionary<string, object>();
                foreach (System.Data.DataColumn dc in dt.Columns)
                {
                    if (dc.DataType == typeof(DateTime))
                    {
                        drow.Add(dc.ColumnName, Convert.ToDateTime(dr[dc.ColumnName]).ToString("yyyy-MM-dd HH:mm:ss"));
                    }
                    else
                    {
                        drow.Add(dc.ColumnName, dr[dc.ColumnName]);
                    }
                }
                list.Add(drow);
            };
            return list;
        }

        /// <summary>
        /// 将两个列不同(结构不同)的DataTable合并成一个新的DataTable
        /// </summary>
        /// <param name="DataTable1">表1</param>
        /// <param name="DataTable2">表2</param>
        /// <param name="DTName">合并后新的表名</param>
        /// <returns>合并后的新表</returns>
        public static DataTable UnionDataTable(this DataTable DataTable1, DataTable DataTable2, string DTName = "")
        {
            DataTable newDataTable = new DataTable();
            if (DataTable1.Rows.Count > DataTable2.Rows.Count)
                newDataTable = FillData(DataTable1, DataTable2);
            else
                newDataTable = FillData(DataTable2, DataTable1);
            if (string.IsNullOrEmpty(DTName) == false)
                newDataTable.TableName = DTName; //设置DT的名字
            return newDataTable;
        }

        private static DataTable FillData(DataTable dt1, DataTable dt2)
        {
            //克隆DataTable1的结构
            DataTable newDataTable = dt1.Clone();
            for (int i = 0; i < dt2.Columns.Count; i++)
            {
                //再向新表中加入DataTable2的列结构
                newDataTable.Columns.Add(dt2.Columns[i].ColumnName);
            }
            object[] obj = new object[newDataTable.Columns.Count];
            //添加DataTable1的数据
            for (int i = 0; i < dt1.Rows.Count; i++)
            {
                dt1.Rows[i].ItemArray.CopyTo(obj, 0);
                newDataTable.Rows.Add(obj);
            }
            for (int i = 0; i < dt2.Rows.Count; i++)
            {
                DataRow[] dr = newDataTable.Select(newDataTable.Columns[0].ColumnName + "='" + dt2.Rows[i][0].ToString() + "' and " + newDataTable.Columns[1].ColumnName + "='" + dt2.Rows[i][1].ToString() + "'");
                if (dr.Length > 0)
                {
                    for (int j = 0; j < dt2.Columns.Count; j++)
                    {
                        dr[0][j + dt1.Columns.Count] = dt2.Rows[i][j].ToString();
                    }
                }
                else
                {
                    newDataTable.Rows.InsertAt(newDataTable.NewRow(), i);
                    for (int j = 0; j < dt2.Columns.Count; j++)
                        newDataTable.Rows[i][j + dt1.Columns.Count] = dt2.Rows[i][j].ToString();
                }
            }
            return newDataTable;
        }


        /// <summary>
        /// 重命名列名
        /// 列名为空时,如果新命名为空则返回InvalidExpressionException错误,否则根据新命名添加一列空数据列
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="list">Dictionary<ColumnName, newName()></param>
        /// <param name="IsAddNonExists">循环字典如果有表格中不存在的列名,是否添加为空数据列</param>
        public static void RenameColumn(this DataTable dt, Dictionary<string, string> list, bool IsAddNonExists = true)
        {
            //删除不需要的列
            string[] cnames = list.Select(s => s.Key.ToLower()).ToArray();
            List<string> notInNames = new List<string>();
            foreach (DataColumn item in dt.Columns)
            {
                if (!cnames.Contains(item.ColumnName.ToLower()))
                {
                    notInNames.Add(item.ColumnName);
                }
            }
            foreach (string strName in notInNames)
            {
                dt.Columns.Remove(strName);
            }
            int i = 0;
            //重命名列
            foreach (KeyValuePair<string, string> kv in list)
            {
                if (string.IsNullOrEmpty(kv.Key))
                {
                    if (!string.IsNullOrEmpty(kv.Value))
                    {
                        dt.Columns.Add(kv.Value);
                        dt.Columns[kv.Value].SetOrdinal(i++);
                    }
                    else
                        throw new Exception("列名不能为空", new InvalidExpressionException());
                }
                else if (dt.Columns.Contains(kv.Key))
                {
                    dt.Columns[kv.Key].SetOrdinal(i++);
                    dt.Columns[kv.Key].ColumnName = kv.Value;
                }
                else
                {
                    //将datatable中不存在的列加入datatable
                    if (string.IsNullOrEmpty(kv.Value))
                    {
                        dt.Columns.Add(kv.Key);
                        dt.Columns[kv.Key].SetOrdinal(i++);
                    }
                    else
                    {
                        dt.Columns.Add(kv.Value);
                        dt.Columns[kv.Value].SetOrdinal(i++);
                    }
                }
            }
        }
    }
    /// <summary>
    /// DataTable转List类
    /// </summary>
    /// <typeparam name="TOut"></typeparam>
    internal class DataTableToList<TOut> where TOut : new()
    {
        private static Dictionary<DataColumnCollection, Func<DataRow, TOut>> _Dic = new Dictionary<DataColumnCollection, Func<DataRow, TOut>>();
        /// <summary>
        /// 数据转换
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static List<TOut> Trans(DataTable dt)
        {
            Func<DataRow, TOut> _FUNC = null;
            if (!_Dic.ContainsKey(dt.Columns))
            {
                ParameterExpression parameterExpressionP = Expression.Parameter(typeof(DataRow), "p");
                List<MemberBinding> memberBindingListDr = new List<MemberBinding>();

                foreach (var item in typeof(TOut).GetProperties())
                {
                    if (dt.Columns.Contains(item.Name))
                    {
                        MethodInfo method = typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(string) });
                        Expression propertyName = Expression.Convert(Expression.Call(parameterExpressionP, method, new Expression[]
                        {
                            Expression.Constant(item.Name, typeof(string))
                        }), item.PropertyType);
                        MemberBinding memberBindingName = Expression.Bind(item, propertyName);
                        memberBindingListDr.Add(memberBindingName);
                    }
                }

                MemberInitExpression memberInitExpression2 = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingListDr.ToArray());
                Expression<Func<DataRow, TOut>> lambdaDr = Expression.Lambda<Func<DataRow, TOut>>(memberInitExpression2,
                    new ParameterExpression[] { parameterExpressionP });
                _FUNC = lambdaDr.Compile();
                _Dic.Add(dt.Columns, _FUNC);
            }
            else
                _FUNC = _Dic[dt.Columns];

            List<TOut> list = new List<TOut>();
            foreach (DataRow item in dt.Rows)
            {
                list.Add(_FUNC(item));
            }
            return list;
        }
    }
}
